/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package xenon3d.vector;

/**
 * A floating point math package with emphasis on epsilon equality and float
 * trigonometry.
 * @author Volker Everts
 * @version 0.1 - 18.08.2011: Created
 */
public class FMath {

    // <editor-fold defaultstate="collapsed" desc=" Public Constants ">

    /** The default epsilon value. */
    public static final float EPS = 0.0001f;

    /** The value of pi as a float. */
    public static final float PI = (float) Math.PI;

    /** The degrees to radians factor. */
    public static final float RAD = PI / 180.0f;

    /** The radians to degrees factor. */
    public static final float DEG = 180.0f / PI;

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Initialization ">

    /**
     * Static class, therefore private constructor.
     */
    private FMath() {}

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Operators (Epsilon) ">

    /**
     * Check a float value for being equal to zero using epsilon equality.
     * @param value the float value to check
     * @param eps the epsilon value
     * @return true, if value is epsilon equal to zero
     */
    public static boolean isZero(float value, float eps) {
        if (value < 0.0f) return value > -eps;
        return value < eps;
    }


    /**
     * Check a float value for being equal to 1 using epsilon equality.
     * @param value the float value to check
     * @param eps the epsilon value
     * @return true, if value is epsilon equal to 1
     */
    public static boolean isUnit(float value, float eps) {
        if (value < 1.0f) return 1.0f - value < eps;
        return value - 1.0f < eps;
    }

    /**
     * Check if the specified values are equal using epsilon equality.
     * @param a the first value
     * @param b the second value
     * @param eps the epsilon value
     * @return true, if both values is epsilon equal
     */
    public static boolean isEqual(float a, float b, float eps) {
        if (a < b) return b - a < eps;
        return a - b < eps;
    }

    /**
     * Check the specified Normalizable for zero norm using epsilon equality.
     * @param n the Normalizable to check
     * @param eps the epsilon value
     * @return true, if the Normalizable's norm is epsilon equal to zero
     */
    public static boolean isZero(Normalizable n, float eps) {
        float norm = n.normSq();
        if (norm < 0.0f) return norm > -eps;
        return norm < eps;
    }

    /**
     * Check the specified Normalizable for unit norm using epsilon equality.
     * @param n the Normalizable to check
     * @param eps the epsilon value
     * @return true, if the Normalizable's norm is epsilon equal to 1
     */
    public static boolean isUnit(Normalizable n, float eps) {
        float norm = n.normSq();
        if (norm < 1.0f) return norm > 1.0f -eps;
        return norm < 1.0f + eps;
    }

    /**
     * Check if the specified Normalizable objects have equal norm using epsilon
     * equality.
     * @param n1 the first Normalizable
     * @param n2 the second Normalizable
     * @param eps the epsilon value
     * @return true, if both Normalizable objects have epsilon equal norm
     */
    public static boolean isEqual(Normalizable n1, Normalizable n2, float eps) {
        float a = n1.normSq();
        float b = n2.normSq();
        if (a < b) return b - a < eps;
        return a - b < eps;
    }

    /**
     * Compares the specified value against zero using epsilon equality.
     * @param a the float value to test
     * @param eps the epsilon value
     * @return -1, if a < 0, or +1, if a > 0, or 0, if a = 0
     */
    public static int compareZero(float a, float eps) {
        if (a < - eps) return -1;
        if (a > + eps) return 1;
        return 0;
    }

    /**
     * Compares the specified value against 1 using epsilon equality.
     * @param a the float value to test
     * @param eps the epsilon value
     * @return -1, if a < 1 or +1, if a > 1, or 0, if a = 1
    */
    public static int compareUnit(float a, float eps) {
        if (a < 1.0f - eps) return -1;
        if (a > 1.0f + eps) return 1;
        return 0;
    }
    /**
     * Compares the specified values using epsilon equality.
     * @param a the first value
     * @param b the second value
     * @param eps the epsilon value
     * @return -1, if a < b, +1, if a > b, and 0, if a = b
    */
    public static int compareEqual(float a, float b, float eps) {
        if (a < b - eps) return -1;
        if (a > b + eps) return 1;
        return 0;
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Operators (General Math) ">

    /**
     * Converts an angle given in degrees to radians.
     * @param angle the angle in degrees
     * @return the angle in radians
     */
    public static float toRadians(float angle) {
        return angle * RAD;
    }

    /**
     * Converts an angle given in radians to degrees.
     * @param angle the angle in radians
     * @return the angle in degrees
     */
    public static float toDegrees(float angle) {
        return angle * DEG;
    }

    /**
     * Returns the absolute value of the specified float number.
     * @param value the float number
     * @return the absolute value
     */
    public static float abs(float value) {
        if (value < 0.0f) return -value;
        return value;
    }

    /**
     * Computes the square root of the specified float value.
     * @param value the float number
     * @return the square root of the number
     */
    public static float sqrt(float value) {
        return (float) Math.sqrt(value);
    }

    /**
     * Computes the maximum of two float values.
     * @param x the first value
     * @param y the second value
     * @return the maximum of x and y
     */
    public static float max(float x, float y) {
        return x > y ? x : y;
    }

    /**
     * Computes the maximum of three float values.
     * @param x the first value
     * @param y the second value
     * @param z the third value
     * @return the maximum of x, y and z
     */
    public static float max(float x, float y, float z) {
        float m = x > y ? x : y;
        return m > z ? m : z;
    }

    /**
     * Computes the maximum of four float values.
     * @param x the first value
     * @param y the second value
     * @param z the third value
     * @param w the third value
     * @return the maximum of x, y, z and w
     */
     public static float max(float x, float y, float z, float w) {
        float m1 = x > y ? x : y;
        float m2 = z > w ? z : w;
        return m1 > m2 ? m1 : m2;
    }

    /**
     * Computes the minimum of two float values.
     * @param x the first value
     * @param y the second value
     * @return the minimum of x and y
     */
    public static float min(float x, float y) {
        return x < y ? x : y;
    }

    /**
     * Computes the minimum of three float values.
     * @param x the first value
     * @param y the second value
     * @param z the third value
     * @return the minimum of x, y and z
     */
    public static float min(float x, float y, float z) {
        float m = x < y ? x : y;
        return m < z ? m : z;
    }

    /**
     * Computes the minimum of four float values.
     * @param x the first value
     * @param y the second value
     * @param z the third value
     * @param w the third value
     * @return the minimum of x, y, z and w
     */
    public static float min(float x, float y, float z, float w) {
        float m1 = x < y ? x : y;
        float m2 = z < w ? z : w;
        return m1 < m2 ? m1 : m2;
    }

    /**
     * Clamps the specified value x to the range [min, max].
     * @param x the value to clamp
     * @param min the lowest possible x value
     * @param max the highest possible x value
     * @return the clamped value
     */
    public static float clamp(float x, float min, float max) {
        if (x < min) return min;
        if (x > max) return max;
        return x;
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" Public Operators (Trigonometry ">

    /**
     * Computes the sine of the specified float angle in degrees.
     * @param angle the angle value in degrees
     * @return the sine of the specified angle
     */
    public static float sin(float angle) {
        return (float) Math.sin(angle * RAD);
    }

    /**
     * Computes the cosine of the specified float angle in degrees.
     * @param angle the angle value in degrees
     * @return the cosine of the specified angle
     */
    public static float cos(float angle) {
        return (float) Math.cos(angle * RAD);
    }

    /**
     * Computes the tangens of the specified float angle in degrees.
     * @param angle the angle value in degrees
     * @return the tangens of the specified angle
     */
    public static float tan(float angle) {
        return (float) Math.tan(angle * RAD);
    }

    /**
     * Computes the arc sine of a value; the returned angle is in degrees and in
     * the range -90 through 90.
     * @param value a value between -1 and 1
     * @return the arc sine of the value in degrees
     */
    public static float asin(float value) {
        return DEG * (float) Math.asin(value);
    }

    /**
     * Computes the arc cosine of a value; the returned angle is in degrees and
     * in the range 0 through 180.
     * @param value a value between -1 and 1
     * @return the arc cosine of the value in degrees
     */
    public static float acos(float value) {
        return DEG * (float) Math.acos(value);
    }

    /**
     * Returns the arc tangent of a value; the returned angle is in degrees and
     * in the range -90 through 90.
     * @param value the value whose arc tangent is to be returned
     * @return the arc tangent of the value in degrees
     */
    public static float atan(float value) {
        return DEG * (float) Math.atan(value);
    }

    /**
     * Returns the angle theta from the conversion of rectangular coordinates
     * (x, y) to polar coordinates (r, theta). This method computes theta by
     * computing an arc tangent of y/x in the range of -180 to 180 degrees.
     * @param x the x coordinate
     * @param y the y coordinate
     * @return the theta component of the point (r, theta) in polar coordinates
     * that corresponds to the point (x, y) in Cartesian coordinates
     */
    public static float atan2(float x, float y) {
        return (float) Math.atan2(x, y);
    }

    // </editor-fold>

} // end class FMath